package org.lanyu.springainovel.common.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.lanyu.springainovel.common.entity.AiMcp;
import org.lanyu.springainovel.common.entity.ServerConfig;
import org.lanyu.springainovel.common.event.McpConfigRequestEvent;
import org.lanyu.springainovel.common.event.McpConfigResponseEvent;
import org.lanyu.springainovel.common.event.ServerConfigChangeEvent;
import org.lanyu.springainovel.common.util.ConversionUtil;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.spec.*;
import io.modelcontextprotocol.spec.McpSchema.Tool;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 动态MCP客户端管理器 - Spring AI 1.0.0完整实现
 * 
 * <p>根据QuestionVO中的useTools字段控制是否启用MCP功能
 * 
 * @author Spring AI Team
 * @since 1.0.0
 */
@Slf4j
@Service
public class DynamicMcpClientManager implements DisposableBean, ApplicationEventPublisherAware {

    /**
     * 当前连接状态的MCP客户端，key为服务器名，value为McpSyncClient
     */
    private final Map<String, McpSyncClient> mcpClients = new ConcurrentHashMap<>();
    
    /**
     * 当前配置，key为服务器名，value为ServerConfig
     */
    private final Map<String, ServerConfig> currentConfigs = new ConcurrentHashMap<>();
    
    /**
     * 定时任务调度�?     */
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
    
    /**
     * 应用事件发布�?     */
    private ApplicationEventPublisher eventPublisher;
    
    /**
     * AiMcp服务
     */
    private final AiMcpService aiMcpService;
    
    /**
     * ObjectMapper用于JSON解析
     */
    private final ObjectMapper objectMapper = new ObjectMapper();

    public DynamicMcpClientManager(AiMcpService aiMcpService) {
        this.aiMcpService = aiMcpService;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.eventPublisher = applicationEventPublisher;
    }

    @EventListener
    public void handleContextRefreshedEvent(ContextRefreshedEvent event) {
        log.info("应用上下文已刷新，开始加载MCP配置...");
        loadSpringAiMcpConfiguration();
        // 健康检查：30秒一�?
        scheduler.scheduleWithFixedDelay(this::performHealthCheck, 30, 30, TimeUnit.SECONDS);
        // 配置检查：5分钟一�?
        scheduler.scheduleWithFixedDelay(this::checkConfigurations, 60, 300, TimeUnit.SECONDS);
    }

    @EventListener
    public void handleMcpConfigResponseEvent(McpConfigResponseEvent event) {
        if (event.isSuccess() && event.getMcpList() != null) {
            processMcpConfigResponse(event.getMcpList());
        } else {
            log.error("处理MCP配置响应失败: {}", event.getErrorMessage());
        }
    }

    @EventListener
    public void handleServerConfigChangeEvent(ServerConfigChangeEvent event) {
        String serverName = event.getServerName();
        if (event.isRemove()) {
            // 移除服务器配�?
            removeServerConfig(serverName);
        } else if (event.isAdd()) {
            // 添加或更新服务器配置
            ServerConfig config = event.getConfig();
            if (config != null) {
                addOrUpdateServerConfig(serverName, config);
            }
        }
    }

    /**
     * 处理MCP配置响应
     */
    private void processMcpConfigResponse(List<AiMcp> mcpList) {
        Map<String, ServerConfig> newConfigs = new ConcurrentHashMap<>();
        try {
            for (AiMcp mcp : mcpList) {
                if (mcp.getConfigJson() == null || mcp.getConfigJson().isEmpty()) {
                    log.warn("MCP配置[{}]缺少configJson，跳过", mcp.getName());
                    continue;
                }
                try {
                    JsonNode root = objectMapper.readTree(mcp.getConfigJson());
                    JsonNode serversNode = root.path("mcpServers");
                    if (serversNode.isMissingNode() || !serversNode.isObject()) {
                        log.warn("MCP配置[{}]的mcpServers字段缺失或格式错误，跳过", mcp.getName());
                        continue;
                    }
                    serversNode.fields().forEachRemaining(entry -> {
                        String serverName = entry.getKey();
                        JsonNode serverNode = entry.getValue();
                        String type = serverNode.path("type").asText("");
                        ServerConfig config = ConversionUtil.parseServerConfigFromJson(serverName, serverNode, type);
                        if (config != null) {
                            newConfigs.put(serverName, config);
                        }
                    });
                } catch (Exception e) {
                    log.warn("MCP配置[{}]解析configJson失败: {}，跳过", mcp.getName(), e.getMessage());
                }
            }
        } catch (Exception e) {
            log.error("处理MCP配置响应失败", e);
        }
        // 只有配置实际变更时才更新
        if (!newConfigs.equals(currentConfigs)) {
            updateServerConfigs(newConfigs);
        } else {
            log.debug("MCP配置无变更，无需更新");
        }
    }

    /**
     * 从数据库加载MCP配置，通过事件机制请求配置
     */
    public void loadSpringAiMcpConfiguration() {
        log.info("请求加载MCP配置(只加载enable=1的服�?");
        if (eventPublisher != null) {
            log.info("事件发布器已初始化，正在发布McpConfigRequestEvent事件...");
            eventPublisher.publishEvent(new McpConfigRequestEvent(this, McpConfigRequestEvent.ConfigRequestType.ALL_ENABLED));
            log.info("McpConfigRequestEvent事件已发布");
        } else {
            log.error("事件发布器未初始化，无法请求MCP配置");
        }
    }

    /**
     * 更新服务器配�?     */
    public synchronized void updateServerConfigs(Map<String, ServerConfig> newConfigs) {
        log.info("更新MCP服务器配置，�?{} 个服务器", newConfigs.size());

        // 移除不再存在的服务器
        currentConfigs.keySet().removeIf(serverName -> {
            if (!newConfigs.containsKey(serverName)) {
                log.info("移除MCP服务�? {}", serverName);
                removeServerConfig(serverName);
                return true;
            }
            return false;
        });

        // 添加新服务器或更新现有服务器
        for (Map.Entry<String, ServerConfig> entry : newConfigs.entrySet()) {
            String serverName = entry.getKey();
            ServerConfig newConfig = entry.getValue();
            
            if (!currentConfigs.containsKey(serverName)) {
                log.info("添加新的MCP服务�? {}", serverName);
            } else if (!currentConfigs.get(serverName).equals(newConfig)) {
                log.info("更新MCP服务器配�? {}", serverName);
                // 先移除旧的客户端
                removeServerConfig(serverName);
            }
            
            currentConfigs.put(serverName, newConfig);
            // 创建并连接MCP客户�?
            createAndConnectMcpClient(serverName, newConfig);
        }

        log.info("MCP服务器配置更新完成，当前配置服务器数: {}", currentConfigs.size());
    }

    /**
     * 添加或更新服务器配置
     */
    private void addOrUpdateServerConfig(String serverName, ServerConfig config) {
        Map<String, ServerConfig> newConfigs = new ConcurrentHashMap<>(currentConfigs);
        newConfigs.put(serverName, config);
        updateServerConfigs(newConfigs);
    }

    /**
     * 移除服务器配�?     */
    private void removeServerConfig(String serverName) {
        // 断开并移除MCP客户�?
        McpSyncClient client = mcpClients.remove(serverName);
        if (client != null) {
            try {
                client.closeGracefully();
                log.info("已断开MCP服务�? {}", serverName);
            } catch (Exception e) {
                log.error("断开MCP服务器[{}]失败", serverName, e);
            }
        }
        // 移除配置
        currentConfigs.remove(serverName);
    }

    /**
     * 创建并连接MCP客户�?     */
    public void createAndConnectMcpClient(String serverName, ServerConfig config) {
        try {
            McpSyncClient client;
            
            // 根据配置类型创建不同的传输方�?
            if (config.isNpxType()) {
                // NPX类型使用Stdio传输
                List<String> commandArgs = new ArrayList<>();
                commandArgs.add(config.getCommand());
                if (config.getArgs() != null) {
                    commandArgs.addAll(config.getArgs());
                }
                
                ServerParameters serverParams = ServerParameters.builder(config.getCommand())
                    .args(commandArgs.subList(1, commandArgs.size()).toArray(new String[0]))
                    .env(config.getEnv())
                    .build();
                
                StdioClientTransport transport = new StdioClientTransport(serverParams);
                
                client = McpClient.sync(transport).build();
            } else {
                // SSE类型使用HttpClientSse传输
                String fullSseUrl = config.getFullSseUrl();
                log.info("连接SSE服务器[{}]，URL: {}", serverName, fullSseUrl);
                
                try {
                    // 使用builder模式创建HttpClientSseClientTransport
                    HttpClientSseClientTransport transport = HttpClientSseClientTransport.builder(fullSseUrl)
                        .build();
                    
                    // 使用McpClient.builder设置更多配置
                    client = McpClient.sync(transport)
                        .clientInfo(new McpSchema.Implementation("novel-serve", "1.0.0"))
                        .build();
                    
                    // 尝试初始化客户端，增加超时控�?
                    try {
                        client.initialize();
                        log.info("成功初始化SSE客户�? {}", serverName);
                    } catch (Exception initEx) {
                        log.error("初始化SSE客户端失�? {}", serverName, initEx);
                        throw initEx;
                    }   
                } catch (Exception e) {
                    log.error("创建SSE传输失败: {}", e.getMessage());
                    throw e;
                }
            }
            
            // 存储客户�?            mcpClients.put(serverName, client);
            
            log.info("成功连接MCP服务�? {}", serverName);
        } catch (Exception e) {
            log.error("连接MCP服务器[{}]失败", serverName, e);
        }
    }

    /**
     * 健康检�?     */
    private void performHealthCheck() {
        log.debug("执行MCP服务器健康检查，当前连接服务器数: {}", mcpClients.size());
        
        for (Map.Entry<String, McpSyncClient> entry : mcpClients.entrySet()) {
            String serverName = entry.getKey();
            McpSyncClient client = entry.getValue();
            
            try {
                // 使用getServerCapabilities方法检查连接状�?
                if (client != null) {
                    try {
                        client.getServerCapabilities();
                        log.debug("MCP服务器[{}]健康检查通过", serverName);
                    } catch (Exception e) {
                        log.warn("MCP服务器[{}]连接异常，尝试重新连�? {}", serverName, e.getMessage());
                        ServerConfig config = currentConfigs.get(serverName);
                        if (config != null) {
                            removeServerConfig(serverName);
                            createAndConnectMcpClient(serverName, config);
                        }
                    }
                }
            } catch (Exception e) {
                log.warn("MCP服务器[{}]健康检查失�? {}", serverName, e.getMessage());
                // 尝试重新连接
                ServerConfig config = currentConfigs.get(serverName);
                if (config != null) {
                    removeServerConfig(serverName);
                    createAndConnectMcpClient(serverName, config);
                }
            }
        }
    }

    /**
     * 配置检�?     */
    private void checkConfigurations() {
        log.debug("执行MCP配置检查，当前配置服务器数: {}", currentConfigs.size());
        // 可以在这里实现配置变更检查逻辑
    }

    /**
     * 获取可用的工具回�?- 根据useTools参数控制
     */
    public List<ToolCallback> getAvailableToolCallbacks() {
        return getAvailableToolCallbacks(true);
    }

    /**
     * 获取可用的工具回�?- 根据useTools参数控制
     */
    public List<ToolCallback> getAvailableToolCallbacks(boolean useTools) {
        log.debug("获取可用工具回调，useTools: {}", useTools);
        
        // 如果用户未启用工具，返回空列�?
        if (!useTools) {
            log.debug("用户未启用工具，返回空列表");
            return new ArrayList<>();
        }
        
        List<ToolCallback> toolCallbacks = new ArrayList<>();
        
        for (Map.Entry<String, McpSyncClient> entry : mcpClients.entrySet()) {
            String serverName = entry.getKey();
            McpSyncClient client = entry.getValue();
            
            try {
                if (client != null) {
                    // 获取服务器工具列�?
                    List<Tool> tools = client.listTools().tools();
                    
                    // 转换为ToolCallback
                    for (Tool tool : tools) {
                        toolCallbacks.add(new McpToolCallback(serverName, client, tool, objectMapper));
                    }
                    
                    log.debug("从MCP服务器[{}]获取到{}个工具", serverName, tools.size());
                }
            } catch (Exception e) {
                log.error("获取MCP服务器[{}]工具列表失败", serverName, e);
            }
        }
        
        log.info("共获取到{}个MCP工具回调", toolCallbacks.size());
        return toolCallbacks;
    }

    /**
     * 根据MCP名称获取工具
     */
    public List<Object> getToolByMcpName(String mcpName) {
        log.debug("根据MCP名称获取工具，名�? {}", mcpName);
        
        McpSyncClient client = mcpClients.get(mcpName);
        if (client == null) {
            log.warn("未找到MCP服务�? {}", mcpName);
            return new ArrayList<>();
        }
        
        try {
            List<Tool> tools = client.listTools().tools();
            log.debug("从MCP服务器[{}]获取到{}个工具", mcpName, tools.size());
            return tools.stream().map(tool -> Map.of(
                "name", tool.name(),
                "description", tool.description()
            )).collect(Collectors.toList());
        } catch (Exception e) {
            log.error("获取MCP服务器[{}]工具列表失败", mcpName, e);
            return new ArrayList<>();
        }
    }

    /**
     * 刷新工具回调
     */
    public void refreshToolCallbacks() {
        log.info("刷新工具回调");
        // 重新连接所有MCP客户�?
        Map<String, ServerConfig> configs = new ConcurrentHashMap<>(currentConfigs);
        updateServerConfigs(configs);
    }

    /**
     * 刷新所有客户端 - 与McpToolController兼容的方�?     */
    public void refreshAllClients() {
        log.info("刷新所有MCP客户端");
        refreshToolCallbacks();
    }

    /**
     * 获取指定服务器的工具列表
     */
    public List<ToolCallback> getToolsForServer(String serverName) {
        log.debug("获取服务器[{}]的工具列", serverName);
        
        McpSyncClient client = mcpClients.get(serverName);
        if (client == null) {
            log.warn("未找到MCP服务�? {}", serverName);
            return new ArrayList<>();
        }
        
        try {
            List<Tool> tools = client.listTools().tools();
            List<ToolCallback> toolCallbacks = new ArrayList<>();
            
            // 转换为ToolCallback
            for (Tool tool : tools) {
                toolCallbacks.add(new McpToolCallback(serverName, client, tool, objectMapper));
            }
            
            log.debug("从MCP服务器[{}]获取到{}个工", serverName, tools.size());
            return toolCallbacks;
        } catch (Exception e) {
            log.error("获取MCP服务器[{}]工具列表失败", serverName, e);
            return new ArrayList<>();
        }
    }

    /**
     * 获取工具的输入模�?     */
    public String getInputSchemaForTool(String toolName) {
        log.debug("获取工具[{}]的输入模", toolName);
        
        // 遍历所有客户端查找工具
        for (Map.Entry<String, McpSyncClient> entry : mcpClients.entrySet()) {
            String serverName = entry.getKey();
            McpSyncClient client = entry.getValue();
            
            try {
                if (client != null) {
                    List<Tool> tools = client.listTools().tools();
                    
                    for (Tool tool : tools) {
                        if (tool.name().equals(toolName)) {
                            return tool.inputSchema().toString();
                        }
                    }
                }
            } catch (Exception e) {
                log.error("获取MCP服务器[{}]工具列表失败", serverName, e);
            }
        }
        
        log.warn("未找到工具[{}]", toolName);
        return "{}";
    }

    /**
     * 获取工具所属的服务器名�?     */
    public String getServerNameForTool(String toolName) {
        log.debug("获取工具[{}]所属的服务器名", toolName);
        
        // 遍历所有客户端查找工具
        for (Map.Entry<String, McpSyncClient> entry : mcpClients.entrySet()) {
            String serverName = entry.getKey();
            McpSyncClient client = entry.getValue();
            
            try {
                if (client != null) {
                    List<Tool> tools = client.listTools().tools();
                    
                    for (Tool tool : tools) {
                        if (tool.name().equals(toolName)) {
                            return serverName;
                        }
                    }
                }
            } catch (Exception e) {
                log.error("获取MCP服务器[{}]工具列表失败", serverName, e);
            }
        }
        
        log.warn("未找到工具[{}]", toolName);
        return "unknown";
    }

    @Override
    public void destroy() throws Exception {
        log.info("正在销毁DynamicMcpClientManager...");
        scheduler.shutdown();
        try {
            if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                scheduler.shutdownNow();
            }
        } catch (InterruptedException e) {
            scheduler.shutdownNow();
            Thread.currentThread().interrupt();
        }
        
        // 关闭所有MCP客户�?
        for (Map.Entry<String, McpSyncClient> entry : mcpClients.entrySet()) {
            String serverName = entry.getKey();
            McpSyncClient client = entry.getValue();
            try {
                if (client != null) {
                    client.closeGracefully();
                    log.info("已关闭MCP服务�? {}", serverName);
                }
            } catch (Exception e) {
                log.error("关闭MCP服务器[{}]失败", serverName, e);
            }
        }
        mcpClients.clear();
        currentConfigs.clear();
        
        log.info("DynamicMcpClientManager已销毁");
    }

    /**
     * MCP工具回调实现
     */
    private static class McpToolCallback implements ToolCallback {
        private final String serverName;
        private final McpSyncClient client;
        private final Tool tool;
        private final ObjectMapper objectMapper;

        public McpToolCallback(String serverName, McpSyncClient client, Tool tool, ObjectMapper objectMapper) {
            this.serverName = serverName;
            this.client = client;
            this.tool = tool;
            this.objectMapper = objectMapper;
        }

        @Override
        public ToolDefinition getToolDefinition() {
            return ToolDefinition.builder()
                    .name(tool.name())
                    .description(tool.description())
                    .inputSchema(convertJsonSchemaToString(tool.inputSchema()))
                    .build();
        }
        
        /**
         * 将JsonSchema转换为JSON字符�?         */
        private String convertJsonSchemaToString(Object jsonSchema) {
            try {
                if (jsonSchema == null) {
                    return "{}";
                }
                
                // 使用ObjectMapper将JsonSchema对象转换为JSON字符�?
                return objectMapper.writeValueAsString(jsonSchema);
            } catch (Exception e) {
                log.warn("无法将JsonSchema转换为JSON字符�? {}", e.getMessage());
                return "{}";
            }
        }

        @Override
        public String call(String toolInput) {
            try {
                // 解析输入参数
                Map<String, Object> arguments = new HashMap<>();
                if (toolInput != null && !toolInput.trim().isEmpty()) {
                    // 尝试解析JSON输入
                    try {
                        ObjectMapper objectMapper = new ObjectMapper();
                        arguments = objectMapper.readValue(toolInput, new TypeReference<Map<String, Object>>() {});
                    } catch (Exception e) {
                        log.warn("无法解析工具输入参数: {}", toolInput, e);
                        // 如果解析失败，将输入作为单个参数
                        arguments.put("input", toolInput);
                    }
                }

                // 创建工具调用请求
                McpSchema.CallToolRequest request = new McpSchema.CallToolRequest(
                    tool.name(),
                    arguments
                );

                // 调用工具
                McpSchema.CallToolResult result = client.callTool(request);
                
                // 处理结果
                if (result.content() != null && !result.content().isEmpty()) {
                    StringBuilder sb = new StringBuilder();
                    for (McpSchema.Content content : result.content()) {
                        if (content instanceof McpSchema.TextContent) {
                            sb.append(((McpSchema.TextContent) content).text());
                        }
                    }
                    return sb.toString();
                } else {
                    return "工具执行完成，但没有返回内容";
                }
            } catch (Exception e) {
                log.error("调用工具 {} 失败", tool.name(), e);
                return "工具调用失败: " + e.getMessage();
            }
        }
    }
}
